package ru.ifmo.genetics.crossover.string;

import org.uncommons.maths.number.ConstantGenerator;
import org.uncommons.maths.number.NumberGenerator;
import org.uncommons.maths.random.Probability;
import org.uncommons.watchmaker.framework.operators.AbstractCrossover;

import java.util.Arrays;
import java.util.List;
import java.util.Random;

/**
 * @author Roman Kolganov
 *         03.12.11
 */
public class VaryingLengthStringCrossover extends AbstractCrossover<String> {

    private final NumberGenerator<Integer> maxLength;

    public VaryingLengthStringCrossover(NumberGenerator<Integer> maxLength) {
        super(1);
        this.maxLength = maxLength;
    }

    public VaryingLengthStringCrossover(Probability crossoverProbability, NumberGenerator<Integer> maxLength) {
        super(1, crossoverProbability);
        this.maxLength = maxLength;
    }

    public VaryingLengthStringCrossover(NumberGenerator<Probability> crossoverProbabilityVariable, NumberGenerator<Integer> maxLength) {
        super(new ConstantGenerator<Integer>(1), crossoverProbabilityVariable);
        this.maxLength = maxLength;
    }

    @Override
    protected List<String> mate(String parent1, String parent2, int numberOfCrossoverPoints, Random rng) {
        // [0, cutPoint)
        int cutPoint1 = rng.nextInt(parent1.length()) + 1;
        // cutPoint1 + (length2 - cutPoint2) <= maxLength
        int minCutPoint2 = Math.max(1, cutPoint1 + parent2.length() - maxLength.nextValue());
        // cutPoint2 + (length1 - cutPoint1) <= maxLength
        int maxCutPoint2 = Math.min(parent2.length(), cutPoint1 - parent1.length() + maxLength.nextValue());
        int cutPoint2 = rng.nextInt(maxCutPoint2 - minCutPoint2 + 1) + minCutPoint2;
        return Arrays.asList(parent1.substring(0, cutPoint1) + parent2.substring(cutPoint2),
                parent2.substring(0, cutPoint2) + parent1.substring(cutPoint1));
    }

}
